#ifndef __LINMATRIX
#define __LINMATRIX

#include "linlist.h"



template <class DATA>
struct MLink
{
   LinList<DATA> *d;                    	// pointer to the element
   //bool			doDelete;			// flag to delete DATA in destructor   
   MLink<DATA> *next;          	// pointer on the next element of the list
   MLink<DATA> *prev;          	// pointer on previous element of the list

   MLink();
   ~MLink();
};

template <class DATA>
MLink<DATA>::MLink()
{
	d = NULL;
	next = prev = NULL;
   
}

template <class DATA>
MLink<DATA>::~MLink()
{
	delete d;
}



template <class DATA>
class LinMatrix
{
protected:
    MLink<DATA> *first;         // first line
    MLink<DATA> *last;          // last line
    int anz;                    // number of lines
	//int anz_r;                    // number of row
    MLink<DATA> *akt;           // pointer on the current element
    int akt_index;              // Index of the curent elemnt
   
public:
    LinMatrix();
    virtual ~LinMatrix();
    virtual int get_num() 	     // return size of the list
        { return anz; }         

    virtual void check();       // check if the list is correct (size match the number of elem)

    virtual void insert(LinList<DATA> *f);	// insert data to the head
	 //virtual void noDelete();	  // DATA wird beim loeschen nicht beruecksichtigt
    virtual bool erase();       // delete the current element from the list

	virtual DATA * get_element(int i, int j);  // return the i-th Element

    virtual LinList<DATA> * get(int i);  // return the i-th Element
    virtual LinList<DATA> * get_first(); // return the first element
    virtual LinList<DATA> * get_last();  // return the last element
    virtual LinList<DATA> * get_next();  // return the next element of the current element
    virtual LinList<DATA> * get_prev();  // return the previous element of the current element

    //make resize(int, int)
	 virtual void resize(int i, int k, DATA defaultValue); 
	//make set(int, int, data)
	 virtual bool set(int i, int k, DATA *d); 
	
};

template <class DATA>
LinMatrix<DATA>::LinMatrix()
{
    anz = 0;
    akt_index = -1;
    akt = first = last = NULL;
    
}

template <class DATA>
LinMatrix<DATA>::~LinMatrix()
{
	MLink<DATA> *hf;

   // Elemente freigeben
   akt = first;
   while (akt != NULL)
   {
		hf = akt->next;
		delete akt;
		akt = hf;
   }
}


template <class DATA>
void LinMatrix<DATA>::insert(LinList<DATA> *f)
{
    MLink<DATA> *sd;

    // new link
    sd = new MLink<DATA>();
    //create copy constructor ?
	sd->d = f;

    // put in the list
    sd->next = first;
    sd->prev = NULL;
    if (first != NULL)
	first->prev = sd;

    // first, last ans size update
    anz++;
    first = sd;
    if (last == NULL)
	last = sd;

    // Position ist undefiniert
    akt = NULL;
    akt_index = -1;
}



template <class DATA>
bool LinMatrix<DATA>::erase()
{
    MLink<DATA> *n_akt;

    // if list empty or undefined
    if (akt)
    {
	// if current element is the first element
	if (akt == first)
	{
	    // if also the last element, mean one element list
	    if (akt == last)
	    {
		akt_index = -1;
		first = last = NULL;
		n_akt = NULL;
	    }
	    else
	    {
		// current element is the first but not the only one
		(akt->next)->prev = NULL;
		first = akt->next;
		n_akt = first;
		akt_index = 0;
	    }
	}
	else
	{
	    // if is the last element
	    if (akt == last)
	    {
		(akt->prev)->next = NULL;
		last = akt->prev;
		n_akt = NULL;
		akt_index = -1;
	    }
	    else
	    // Element is in the middle of the list
	    {
		(akt->next)->prev = akt->prev;
		(akt->prev)->next = akt->next;
		n_akt = akt->next;
		akt_index++;
	    }
	}

	// delete the object
	delete akt;

	// update current element pointer
	akt = n_akt;

	// update size
	anz--;
	return true;
    }
    return false;
}

template <class DATA>
DATA * LinMatrix<DATA>::get_element(int i, int j){
	LinList<DATA>* tempLin;
	tempLin = LinMatrix<DATA>::get(i);

	DATA * tempData;
	tempData = tempLin->get(j);
	
	return tempData;
}



template <class DATA>
LinList<DATA>* LinMatrix<DATA>::get(int i)
// return the i-th element of the list
{
    bool ahead;   // go next if it's true, previous if false
    int j;

    // if i is out of the list
    if (i >= anz)
	return NULL;

    // if i is the current element
    if (i == akt_index)
	return akt->d;

    // if not positionned yet
    if (akt_index == -1)
    {
	// test to know if begin from begining or from the end
	if (i < (anz / 2))
	{
	    akt = first;
	    akt_index = 0;
	    ahead = true;
	}
	else
	{
	    akt = last;
	    akt_index = anz - 1;
	    ahead = false;

	}
    }
    else
    {
	// test with current position 
	if (i < akt_index)
	{
	    // i closest from begin or current position
	    if ((akt_index - i) > i)
	    {
		akt = first;
		akt_index = 0;
		ahead = true;
	    }
	    else
		ahead = false;
	}
	else
	{
	    // i closest from end or current position
	    if ((i - akt_index) > ((anz-1) - i))
	    {
		akt = last;
		akt_index = anz - 1;
		ahead = false;
	    }
	    else
		ahead = true;
	}
    }


    // set position for i 
    if (ahead)
    {
	for (j = akt_index; j < i; j++)
	{
	    if (!akt)
		cout << "LinMatrix::get: List seems to be inkonsistent"  << endl;
	    akt = akt->next;
	}
    }
    else
    {
	for (j = akt_index; j > i; j--)
	{
	    if (!akt)
		cout << "LinMatrix::get: List seems to be inkonsistent" << endl;
	    akt = akt->prev;
	}
    }

    akt_index = i;
    return akt->d;
}


template <class DATA>
LinList<DATA>* LinMatrix<DATA>::get_first()
{
    akt = first;

    if (akt != NULL)
    {
	akt_index = 0;
	return akt->d;
    }
    else
	return NULL;
}




template <class DATA> 
LinList<DATA>* LinMatrix<DATA>::get_last()
{
    akt = last;

    if (akt != NULL)
    {
	akt_index = anz - 1;
	return akt->d;
    }
    else
	return NULL;
}

template <class DATA> 
LinList<DATA>* LinMatrix<DATA>::get_next()
{
	if (akt==NULL) return NULL;
    akt = akt->next;
    if (akt != NULL)
    {
	akt_index++;
	return akt->d;
    }
    else
    {
	akt_index = -1;
	return NULL;
    }
}

template <class DATA>
LinList<DATA>* LinMatrix<DATA>::get_prev()
{
	if (akt==NULL) return NULL;
    akt = akt->prev;
    if (akt != NULL)
    {
	akt_index--;
	return akt->d;
    }
    else
    {
	akt_index = -1;
	return NULL;
    }
}

//add more check e.g same number rows/lines
template <class DATA> 
void LinMatrix<DATA>::check()
{
    MLink<DATA> *f, *old_f;
    int myanz;
    char buffer[255];

    old_f = first;
    // if first = null but not empty list : error
    if (old_f == NULL)
    {
		if (last != NULL)
			cout << "LinMatrix::check: first == NULL, last != NULL"  << endl;
		if (anz != 0)
			cout << "LinMatrix::check: first == NULL, anz != 0" << endl;
	return;
    }

    myanz = 1;
    if (old_f->prev != NULL)
    {
	cout << "LinMatrix::check: Listenkopf.prev ungleich NULL" << endl;
	return;
    }

    for (f = old_f->next; f != NULL; f = f->next)
    {
	if (f->prev != old_f)
	{
	    cout << "LinMatrix::check: Rueckwaertsverkettung fehlerhaft" << endl;
            return;
        }
	if (old_f->next != f)
	{
	    cout << "LinMatrix::check: Vorwaertsverkettung fehlerhaft" << endl;
            return;
        }
	old_f = f;

	myanz ++;
	if (myanz > anz)
	{
	    sprintf(buffer, "LinMatrix::check: anz (%d != %d) passt nicht", myanz, anz);
	    cout << buffer << endl;
	    return;
	}
    }

    if (old_f->next != NULL)
    {
	cout << "LinMatrix::check: Listenende.next ungleich NULL" << endl;
	return;
    }

    if (last != old_f)
    {
	cout << "LinMatrix::check: last ungleich Listenende" << endl;
	return;
    }

    if (myanz != anz)
    {
	sprintf(buffer, "LinMatrix::check: anz (%d != %d) passt nicht", myanz, anz);
	cout << buffer  << endl;
    }
}


//resize (only for the linMatrix : DATA = double)
template <class DATA> 
void LinMatrix<DATA>::resize(int i, int k, DATA defaultValue){
	//double tempValue = 0; //we insert 0 because we are looking for values close or supp to 1 in the sim matrix
	cout << "before for matrix" << k << " times linlist of " << i << endl;
	system("pause");
	for(int j=0; j<k; j++){
		
		LinList<DATA>* tempList = new LinList<DATA>();
		tempList->resize(i, defaultValue);
		insert(tempList);
		cout << "in for list " << j << endl;
	}
	cout << "after for matrix" << endl;
}
	 
//set (only for the linMatrix : DATA = double
template <class DATA> 
bool LinMatrix<DATA>::set(int k, int i, DATA *d){//I switched i and k to keep the same name of variables in the code
	bool ahead;   // go next if it's true, previous if false
    int j;

    // if i is out of the list
    if (i >= anz)
	return false;

    // if i is the current element
	if (i == akt_index){
		akt->d->set(k, d);
		return true;
	}
	

    // if not positionned yet
    if (akt_index == -1)
    {
	// test to know if begin from begining or from the end
	if (i < (anz / 2))
	{
	    akt = first;
	    akt_index = 0;
	    ahead = true;
	}
	else
	{
	    akt = last;
	    akt_index = anz - 1;
	    ahead = false;

	}
    }
    else
    {
	// test with current position 
	if (i < akt_index)
	{
	    // i closest from begin or current position
	    if ((akt_index - i) > i)
	    {
		akt = first;
		akt_index = 0;
		ahead = true;
	    }
	    else
		ahead = false;
	}
	else
	{
	    // i closest from end or current position
	    if ((i - akt_index) > ((anz-1) - i))
	    {
		akt = last;
		akt_index = anz - 1;
		ahead = false;
	    }
	    else
		ahead = true;
	}
    }


    // set position for i 
    if (ahead)
    {
	for (j = akt_index; j < i; j++)
	{
	    if (!akt)
		cout << "LinList::get: List seems to be inkonsistent" << endl;
	    akt = akt->next;
	}
    }
    else
    {
	for (j = akt_index; j > i; j--)
	{
	    if (!akt)
		cout << "LinList::get: List seems to be inkonsistent" << endl;
	    akt = akt->prev;
	}
    }

    akt_index = i;
    akt->d->set(k, d);
	return true;
}






#endif // __LINMATRIX